
static char RCSId[] = "$Id: Status.m,v 1.1.1.1 1993/03/18 03:35:28 davis Exp $";


#import <appkit/Font.h>
#import <appkit/nextstd.h>	/* NX_FREE			*/

#import <objc/hashtable.h>	/* NXCopyStringBufferFromZone()	*/
#import <objc/zone.h>

#import <setjmp.h>		/* setjmp() 			*/
#import <stdio.h>
#import <stdlib.h>
#import <strings.h>		/* strcat(), strcpy(), index()	*/

#import "FunctionObject.h"
#import "GnuplotPlot.h"
#import "Status.h"
#import "TicObject.h"


@interface Status (Private)

- (const char *) _copyString:(const char *)source to:(char **)dest;
- _doText:(const char *)theText;
- _setText:(const char *) aString;

@end


/* 
 *  We can't import all of plot.h because of various complications 
 *  (redefinitions, conflicting identifiers, etc.), but we need the 
 *  following structures which are defined there.
 */
struct ticdef {
    int type;				/* one of three constants	*/
    union {
           struct {			/* for TIC_SERIES		*/
                  double start, incr;
                  double end;
	      } series;
           struct ticmark *user;	/* for TIC_USER			*/
       } def;
};

struct ticmark {
    double position;			/* where on axis is this	*/
    char *label;			/* optional string label	*/
    struct ticmark *next;		/* linked list			*/
};



/* 
 *  The class records which Status object was the last to issue a plot 
 *  command to Gnuplot.  We need to know this when we set our instance 
 *  variables to the corresponding Gnuplot values -- see -grabCurrent.
 */
static id lastplot = nil;

#define MAX_LINE_LEN 1024	/* Same as in plot.h	*/
#define DEFAULT_FONT		"Helvetica"

#define CONTOUR_NONE     0       /* Where to place contour maps if at all. */
#define CONTOUR_BASE     1
#define CONTOUR_SRF      2
#define CONTOUR_BOTH     3


/*
 *  All instance variables begin with s_ to avoid confusion with the 
 *  variables defined in gnuplot .c files.
 *
 *  gnuplot Variable (setshow.c)	   Status Instance Variable
 *  ----------------------------	   ------------------------
 */
extern char nextfe_font[];		/* s_font			*/
extern int nextfe_fontsize;		/* s_fontsize			*/
extern char title[];			/* s_title			*/
extern char xlabel[];			/* s_labelCoord[]		*/
extern char ylabel[];
extern char zlabel[];
extern char dummy_var[][51];		/* s_dummy_var[]		*/
extern int parametric;			/* s_parametric			*/
extern int polar;			/* s_polar			*/
extern int draw_border;			/* s_border			*/
extern int draw_surface;		/* s_draw_surface		*/
extern int draw_contour;
extern int contour_kind;		/* s_contour_kind		*/
extern int contour_pts;			/* s_contour_pts		*/
extern int contour_order;		/* s_contour_order		*/
extern int label_contours;		/* s_label_contours		*/
extern int grid;			/* s_grid			*/
extern int xzeroaxis;			/* s_xzeroaxis			*/
extern int yzeroaxis;			/* s_yzeroaxis			*/
extern int tic_in;			/* s_tics_in			*/
extern int xtics, ytics, ztics;		/* s_ticsCoord[]		*/
extern struct ticdef xticdef;		/* s_ticdefCoord[]		*/
extern struct ticdef yticdef;
extern struct ticdef zticdef;
extern int is_log_x, is_log_y, is_log_z;/* s_isLogCoord[]		*/

extern double base_log_x, base_log_y,	/* Currently unused by Status	*/
	      base_log_z;		//
extern double log_base_log_x,
	      log_base_log_y,
	      log_base_log_z;


extern int key;				/* s_key			*/
extern double key_x, key_y, key_z;	/* s_keyCoord[]			*/
extern int timedate;			/* s_time			*/
extern int time_xoffset, time_yoffset;	/* s_timeCoord[]		*/
extern float xsize, ysize;		/* s_xsize, s_ysize		*/
extern float surface_rot_x;		/* s_rot_x			*/
extern float surface_rot_z;		/* s_rot_z			*/
extern int hidden3d;			/* s_hidden3d			*/
extern int autoscale_x;			/* s_autoscaleCoord[]		*/
extern int autoscale_y;
extern int autoscale_z;
extern int autoscale_r;
extern double xmin, xmax;		/* s_minCoord[], s_maxCoord[]	*/
extern double ymin, ymax;
extern double zmin, zmax;
extern double rmin, rmax;
extern int angles_format;		/* degrees			*/
extern int samples;			/* s_samples			*/
extern int samples_1;			/* s_samplesCoord		*/
extern int samples_2;
extern int iso_samples_1;		/* s_iso_samplesCoord		*/
extern int iso_samples_2;
extern char replot_line[];		/* s_plotargs			*/
extern NXStream *EPSStream;		/* s_epsStream (terminal)	*/
extern int nextfe_halve;		/* (GnuplotPlot preference)	*/
extern NEXTFE_reset();			/* Appends PS Trailer to stream	*/


/*
 *  Removes leading blanks and tabs.
 */
static char *_removeLeadingBlanks (char *aString)
{
    char *cur;

    cur = aString;
    if (cur && ((*cur == ' ') || (*cur == '\t')))
        cur++;

    strcpy (aString, cur);
    return aString;
}


/*
 *  Stores the first line, up to max characters,  from the multi-line 
 *  string source in dest and returns a pointer to it.  It replaces 
 *  the newline with a null character.  If the line was the last in 
 *  the string, it returns NULL.
 */
static char *_get_line (char *dest, int max, const char *source)
{
    char *returnVal = dest;

    if (source && dest)  {
	while ((*source != '\n') && (*source != '\0') && (max > 0)) {
	    *dest = *source;
	    dest++;
	    source++;
	    max--;
	}

	*dest = '\0';

	if (*source == '\n')
	    return returnVal;
    }					/*  NULL is returned if line is	*/
					/*  too long or end of source	*/
    return NULL;			/*  or dest string is reached.	*/
}
	

/* 
 *  Frees the list of ticmarks whose head is pointed to by t and sets 
 *  t to NULL.
 */
static void _freeLinkedList(struct ticmark **t)
{
    struct ticmark *cur;

    while (*t) {
	cur = *t;
	*t = (*t)->next;
	free (cur);
    }

    *t = NULL;
    return;
}


@implementation Status

+ lastplot
{
    return lastplot;
}


+ setHalvePlot:(BOOL)condition
{
    nextfe_halve = condition;
    return self;
}


- init
{
    int counter;

    [super init];
    zone = [self zone];

    functions = nil;

    delegate = nil;
    report = YES;

    s_epsStream = NULL;

    /* 
     *  Set all pointers to NULL or nil so -resetCurrent
     *  doesn't try to free them.
     */
    text = s_title = s_plotargs = s_font = appendix =
	s_dummy_var[0] = s_dummy_var[1] = NULL;

    for (counter = 0 ; counter < 3 ; counter++) {
	s_labelCoord[counter] = NULL;
	ticDefs[counter] = nil;
    }

    [self resetCurrent];

    return self;
}


- free
{
    int counter;

    if (s_epsStream)
	NXCloseMemory (s_epsStream, NX_FREEBUFFER);

    /*  
     *  NXZoneFree() doesn't bomb if you give it a NULL pointer, so we 
     *  don't bother to check the pointer.
     */

    NXZoneFree (zone, text);
    NXZoneFree (zone, appendix);

    NXZoneFree (zone, s_title);
    NXZoneFree (zone, s_font);

    NXZoneFree (zone, s_dummy_var[0]);
    NXZoneFree (zone, s_dummy_var[1]);

    for (counter = 0 ; counter < 3 ; counter++) {
	if (ticDefs[counter])
	    [[ticDefs[counter] freeObjects] free];
	NXZoneFree (zone, s_labelCoord[counter]);
    }

    NXZoneFree (zone, s_plotargs);

    if (functions)
	[[functions freeObjects] free];

    lastplot = nil;

    return [super free];
}


- setReport:(BOOL)cond
{
    report = cond;
    return self;
}


- (BOOL)report
{
    return report;
}


- setDelegate: anObject
{
    delegate = anObject;
    return self;
}


- delegate
{
    return delegate;
}


- (NXStream *)stream
{
    return s_epsStream;
}



- (BOOL)canPlot
{
    int count = 0, index;
    int total = [functions count];

    /* Count the number of functions */
    index = total;
    while (index--)
	count += ([[functions objectAt:index] isDataFile]) ? 0 : 1;

    /* 
     *  We can plot iff
     * 
     * 		1) there is an appendix
     * 		
     * 			or
     * 			
     * 		2) there is at least one FunctionObject and
     * 		3) if the plot is parametric then
     * 			a) we have pairs of functions (two-dimensional),
     * 			b) or we have triplets of functions (3-dimens.)
     *
     */
    
    return ((appendix && *appendix) ||
	    (total && 
	     (s_parametric? (isThreeD? !(count % 3)
				     : (!(count % 2) && (total == count)))
			  :YES)));

}


- plot
{
    [self applyCurrent];
    lastplot = self;

    if (areSettingsEdited && !appendix)  {
	[self buildPlotargs];
	[self _setText:s_plotargs];

	areSettingsEdited = NO;
    }

    if (appendix && *appendix)
	[self _setText:appendix];	/* Appendix overrides current plot */

    return [self _doText:text];
}



- saveToFile:(const char *)filename
{
    char *copy, *line, *cur;
    id returnVal;

    /* 
     *  Gnuplot sometimes drops characters off the end of long 
     *  pathnames given to the "save" command, a bug that seems to 
     *  have been introduced between 3.1 and 3.2.  We work around it 
     *  by cd'ing to the directory first and then saving the file.
     */
    copy = NXCopyStringBufferFromZone (filename, zone);

    if (cur = rindex (copy, '/')) {
	*(cur++) = '\0';
	line = NXZoneMalloc (zone, 15 + strlen (filename));
	sprintf (line, "cd '%s'\nsave '%s'\n", copy, cur);
    } else {
	line = NXZoneMalloc (zone, 9 + strlen (filename));
	sprintf (line, "save '%s'\n", filename);
    }

    returnVal = [self _doText:line]? self : nil;
    NXZoneFree (zone, copy);
    NXZoneFree (zone, line);

    return returnVal;
}



- reportSettingsChange:sender
{
    areSettingsEdited = YES;
    lastplot = nil;		/* Last valid plot is no longer valid */

    if (delegate && report)
	[delegate settingsDidChange:self];

    return self;
}



/* 
 *  Resets all the instance variables to their default values.
 */
- resetCurrent
{
    int counter;

    if (functions)
	[functions freeObjects];
    else
	functions = [[List allocFromZone:zone] init];

    NXZoneFree (zone, text);
    text = NULL;

    NXZoneFree (zone, s_title);
    s_title = NULL;

    NXZoneFree (zone, s_dummy_var[0]);
    s_dummy_var[0] = NULL;

    NXZoneFree (zone, s_dummy_var[1]);
    s_dummy_var[1] = NULL;

    for (counter = 0 ; counter < 3 ; counter++)  {
	NXZoneFree (zone, s_labelCoord[counter]);
	s_labelCoord[counter] = NULL;
	s_minCoord[counter] = -10.0;
	s_maxCoord[counter] = 10.0;
	s_isLogCoord[counter] = 0;
	s_ticsCoord[counter] = 1;
	s_ticTypeCoord[counter] = TIC_COMPUTED;
	s_ticSeriesCoord[counter].start = -10.0;
	s_ticSeriesCoord[counter].incr = 5.0;
	s_ticSeriesCoord[counter].end = 10.0;
	if (ticDefs[counter])
	    [ticDefs[counter] freeObjects];
	else
	    ticDefs[counter] = [[DoubleValueSortedList allocFromZone:zone]
						       init];
    }

    s_minCoord[R_TAG] = -0.0;	/* Gnuplot uses -0.0, not sure why	*/
    s_maxCoord[R_TAG] = 10.0;

    degrees = NO;

    NXZoneFree (zone, s_plotargs);
    s_plotargs = NULL;

    NXZoneFree (zone, s_font);
    s_font = NULL;

    NXZoneFree (zone, appendix);
    appendix = NULL;

    s_fontsize = 16;

    isThreeD = NO;
    s_parametric = NO;
    s_polar = NO;
    s_border = YES;
    s_draw_surface = YES;
    contourBase = NO;
    contourSurface = NO;
    s_contour_kind = CONTOUR_KIND_LINEAR;
    s_contour_pts = 5;
    s_contour_order = 4;
    s_label_contours = YES;
    s_hidden3d = NO;
    s_grid = NO;
    s_xzeroaxis = YES;
    s_yzeroaxis = YES;

    s_tics_in = YES;

    s_key = YES;		/* Plot has a key, in default location	*/
    s_key_default = YES;

    s_time = NO;		/* Don't show time/date			*/
    s_time_default = YES;

    for (counter = 0 ; counter < 4 ; counter++)
	s_autoscaleCoord[counter] = 1;

    sizeProp = YES;	/* Keep x, y, z proportional */
    s_xsize = s_ysize = 1.0;
    s_samples = s_samplesCoord[X_TAG] = s_samplesCoord[Y_TAG] = 100;
    s_iso_samplesCoord[X_TAG] = s_iso_samplesCoord[Y_TAG] = 10;

    s_rotCoord[X_TAG] = 60.0;
    s_rotCoord[Z_TAG] = 30.0;

    areSettingsEdited = NO;

    return self;
}





/*  Sets the current gnuplot settings to the Status instance variables.  */
- applyCurrent
{
    if (s_font)
	strcpy (nextfe_font, s_font);
    else
	strcpy (nextfe_font, DEFAULT_FONT);

    nextfe_fontsize = s_fontsize;

    if (s_title)
	strcpy (title, s_title);
    else
	strcpy (title, "");

    if (s_dummy_var[0])
	strcpy (dummy_var[0], s_dummy_var[0]);
    else
	strcpy (dummy_var[0], "x");

    if (s_dummy_var[1])
	strcpy (dummy_var[1], s_dummy_var[1]);
    else
	strcpy (dummy_var[1], "y");

    if (s_labelCoord[X_TAG])
	strcpy (xlabel, s_labelCoord[X_TAG]);
    else
	strcpy (xlabel, "");

    if (s_labelCoord[Y_TAG])
	strcpy (ylabel, s_labelCoord[Y_TAG]);
    else
	strcpy (ylabel, "");

    if (s_labelCoord[Z_TAG])
	strcpy (zlabel, s_labelCoord[Z_TAG]);
    else
	strcpy (zlabel, "");

    parametric = s_parametric;
    polar = s_polar;
    draw_border = s_border;
    draw_surface = s_draw_surface;

    if (!contourBase && !contourSurface)
	draw_contour = CONTOUR_NONE;
    else if (contourBase && contourSurface)
	draw_contour = CONTOUR_BOTH;
    else if (contourBase)
	draw_contour = CONTOUR_BASE;
    else if (contourSurface)
	draw_contour = CONTOUR_SRF;

    contour_kind = s_contour_kind;
    contour_pts = s_contour_pts;
    contour_order = s_contour_order;
    label_contours = s_label_contours;

    hidden3d = s_hidden3d;
    grid = s_grid;
    xzeroaxis = s_xzeroaxis;
    yzeroaxis = s_yzeroaxis;

    tic_in = s_tics_in;
    xtics = s_ticsCoord[X_TAG];
    ytics = s_ticsCoord[Y_TAG];
    ztics = s_ticsCoord[Z_TAG];
    [self applyCurrentTicDefs];

    is_log_x = s_isLogCoord[X_TAG];
    is_log_y = s_isLogCoord[Y_TAG];
    is_log_z = s_isLogCoord[Z_TAG];

    base_log_x = base_log_y = base_log_z = 10.0;
    log_base_log_x = log_base_log_y = log_base_log_z = log(10.0);

    autoscale_x = s_autoscaleCoord[X_TAG];
    autoscale_y = s_autoscaleCoord[Y_TAG];
    autoscale_z = s_autoscaleCoord[Z_TAG];
    autoscale_r = s_autoscaleCoord[R_TAG];

    xmin = s_minCoord[X_TAG];
    xmax = s_maxCoord[X_TAG];
    ymin = s_minCoord[Y_TAG];
    ymax = s_maxCoord[Y_TAG];
    zmin = s_minCoord[Z_TAG];
    zmax = s_maxCoord[Z_TAG];
    rmin = s_minCoord[R_TAG];
    rmax = s_maxCoord[R_TAG];

    angles_format = degrees? ANGLES_DEGREES :ANGLES_RADIANS;

    key = s_key? (s_key_default? -1 :1) :0;
    key_x = s_keyCoord[X_TAG];
    key_y = s_keyCoord[Y_TAG];
    key_z = s_keyCoord[Z_TAG];

    timedate = s_time? 1 :0;
    if (s_time && !(s_title && *s_title))
	strcpy (title, " ");	// Kludge to make time visible if no title
    time_xoffset = (s_time_default? 0 :s_timeCoord[X_TAG]);
    time_yoffset = (s_time_default? 0 :s_timeCoord[Y_TAG]);

    xsize = s_xsize;
    ysize = s_ysize;

    surface_rot_x = s_rotCoord[X_TAG];
    surface_rot_z = s_rotCoord[Z_TAG];

    samples = s_samples;
    samples_1 = s_samplesCoord[X_TAG];
    samples_2 = s_samplesCoord[Y_TAG];
    iso_samples_1 = s_iso_samplesCoord[X_TAG];
    iso_samples_2 = s_iso_samplesCoord[Y_TAG];

    return self;
}



- applyCurrentTicDefs
{
    int count;
    struct ticmark *new;
    id tic;

    switch (s_ticTypeCoord[X_TAG]) {
    case TIC_SERIES:
	xticdef.def.series.start = s_ticSeriesCoord[X_TAG].start;
	xticdef.def.series.incr = s_ticSeriesCoord[X_TAG].incr;
	xticdef.def.series.end = s_ticSeriesCoord[X_TAG].end;
	break;
    case TIC_USER:
	if (xticdef.type == TIC_USER)
	    _freeLinkedList (&(xticdef.def.user));
	else
	    xticdef.def.user = NULL;
	if ((count = [ticDefs[X_TAG] count] - 1) >= 0) {
	    new = xticdef.def.user = malloc (sizeof (struct ticmark));
	    while (count >= 0) {
		new->position = [tic = [ticDefs[X_TAG] objectAt:count]
				 doubleValue];
		new->label = (char *) [tic stringValue];
		if (count--) {
		    new->next = malloc (sizeof (struct ticmark));
		    new = new->next;
		} else
		    new->next = NULL;
	    }
	    break;
	}
    }
    xticdef.type = s_ticTypeCoord[X_TAG];

    switch (s_ticTypeCoord[Y_TAG]) {
    case TIC_SERIES:
	yticdef.def.series.start = s_ticSeriesCoord[Y_TAG].start;
	yticdef.def.series.incr = s_ticSeriesCoord[Y_TAG].incr;
	yticdef.def.series.end = s_ticSeriesCoord[Y_TAG].end;
	break;
    case TIC_USER:
	if (yticdef.type == TIC_USER)
	    _freeLinkedList (&(yticdef.def.user));
	else
	    yticdef.def.user = NULL;
	if ((count = [ticDefs[Y_TAG] count] - 1) >= 0) {
	    new = yticdef.def.user = malloc (sizeof (struct ticmark));
	    while (count >= 0) {
		new->position = [tic = [ticDefs[Y_TAG] objectAt:count] 
				 doubleValue];
		new->label = (char *) [tic stringValue];
		if (count--) {
		    new->next = malloc (sizeof (struct ticmark));
		    new = new->next;
		} else
		    new->next = NULL;
	    }
	    break;
	}
    }
    yticdef.type = s_ticTypeCoord[Y_TAG];

    switch (s_ticTypeCoord[Z_TAG]) {
    case TIC_SERIES:
	zticdef.def.series.start = s_ticSeriesCoord[Z_TAG].start;
	zticdef.def.series.incr = s_ticSeriesCoord[Z_TAG].incr;
	zticdef.def.series.end = s_ticSeriesCoord[Z_TAG].end;
	break;
    case TIC_USER:
	if (zticdef.type == TIC_USER)
	    _freeLinkedList (&(zticdef.def.user));
	else
	    zticdef.def.user = NULL;
	if ((count = [ticDefs[Z_TAG] count] - 1) >= 0) {
	    new = zticdef.def.user = malloc (sizeof (struct ticmark));
	    while (count >= 0) {
		new->position = [tic = [ticDefs[Z_TAG] objectAt:count]
				 doubleValue];
		new->label = (char *) [tic stringValue];
		if (count--) {
		    new->next = malloc (sizeof (struct ticmark));
		    new = new->next;
		} else
		    new->next = NULL;
	    }
	    break;
	}

    }
    zticdef.type = s_ticTypeCoord[Z_TAG];

    return self;
}


/* 
 *  Resets all the instance variables to the current gnuplot settings, 
 *  all of which pertain to the most recent plot:
 */
- grabCurrent
{
    char *cur;

    NXZoneFree (zone, s_font);
    s_font = NXCopyStringBufferFromZone (nextfe_font, zone);

    s_fontsize = nextfe_fontsize;

    NXZoneFree (zone, s_title);
    s_title = NXCopyStringBufferFromZone (title, zone);

    NXZoneFree (zone, s_dummy_var[0]);
    s_dummy_var[0] = NXCopyStringBufferFromZone (dummy_var[0], zone);

    NXZoneFree (zone, s_dummy_var[1]);
    s_dummy_var[1] = NXCopyStringBufferFromZone (dummy_var[1], zone);

    NXZoneFree (zone, s_labelCoord[X_TAG]);
    s_labelCoord[X_TAG] = NXCopyStringBufferFromZone (xlabel, zone);

    NXZoneFree (zone, s_labelCoord[Y_TAG]);
    s_labelCoord[Y_TAG] = NXCopyStringBufferFromZone (ylabel, zone);

    NXZoneFree (zone, s_labelCoord[Z_TAG]);
    s_labelCoord[Z_TAG] = NXCopyStringBufferFromZone (zlabel, zone);

    [self grabCurrentTicDefs];

    /*  
     *  If the last plot command was "splot," as opposed to "plot,"  
     *  it is three dimensional.
     */
    isThreeD = (*(_removeLeadingBlanks(replot_line)) == 's');

    s_parametric = parametric;
    s_polar = polar;
    s_border = draw_border;
    s_draw_surface = draw_surface;

    switch (draw_surface) {
    case CONTOUR_NONE:
	contourBase = contourSurface = NO;
	break;
    case CONTOUR_BASE:
	contourBase = YES;
	contourSurface = NO;
	break;
    case CONTOUR_SRF:
	contourBase = NO;
	contourSurface = YES;
	break;
    default:
	contourBase = YES;
	contourSurface = YES;
	break;
    }

    s_contour_kind = contour_kind;
    s_contour_pts = contour_pts;
    s_contour_order = contour_order;
    s_label_contours = label_contours;

    s_hidden3d = hidden3d;
    s_grid = grid;
    s_xzeroaxis = xzeroaxis;
    s_yzeroaxis = yzeroaxis;

    s_tics_in = tic_in;
    s_ticsCoord[X_TAG] = xtics;
    s_ticsCoord[Y_TAG] = ytics;
    s_ticsCoord[Z_TAG] = ztics;

    s_isLogCoord[X_TAG] = is_log_x;
    s_isLogCoord[Y_TAG] = is_log_y;
    s_isLogCoord[Z_TAG] = is_log_z;

    s_autoscaleCoord[X_TAG] = autoscale_x;
    s_autoscaleCoord[Y_TAG] = autoscale_y;
    s_autoscaleCoord[Z_TAG] = autoscale_z;
    s_autoscaleCoord[R_TAG] = autoscale_r;

    s_minCoord[X_TAG] = xmin;
    s_maxCoord[X_TAG] = xmax;
    s_minCoord[Y_TAG] = ymin;
    s_maxCoord[Y_TAG] = ymax;
    s_minCoord[Z_TAG] = zmin;
    s_maxCoord[Z_TAG] = zmax;
    s_minCoord[R_TAG] = rmin;
    s_maxCoord[R_TAG] = rmax;

    degrees = (angles_format == ANGLES_DEGREES);

    s_key = ((key == -1) || (key == 1));
    s_key_default = (key == -1);
    s_keyCoord[X_TAG] = key_x;
    s_keyCoord[Y_TAG] = key_y;
    s_keyCoord[Z_TAG] = key_z;

    s_time = timedate;
    s_time_default = (time_xoffset == 0.0) && (time_yoffset == 0.0);
    s_timeCoord[X_TAG] = time_xoffset;
    s_timeCoord[Y_TAG] = time_yoffset;

    s_xsize = xsize;
    s_ysize = ysize;

    s_samples = samples;
    s_samplesCoord[X_TAG] = samples_1;
    s_samplesCoord[Y_TAG] = samples_2;
    s_iso_samplesCoord[X_TAG] = iso_samples_1;
    s_iso_samplesCoord[Y_TAG] = iso_samples_2;

    s_rotCoord[X_TAG] = surface_rot_x;
    s_rotCoord[Z_TAG] = surface_rot_z;

    NXZoneFree (zone, s_plotargs);
    if (cur = index (replot_line, ' ')) {	/* todo What about tabs? */
	s_plotargs = NXZoneMalloc (zone, strlen (replot_line) + 1);
	strcpy (s_plotargs, cur + 1);
    } else
	s_plotargs = NULL;

    /* Break plotargs into separate functions */
    [self grabFunctionsFrom:s_plotargs];
    [self buildPlotargs];

    [self _setText: s_plotargs];
    areSettingsEdited = YES;

    return self;
}


- grabCurrentTicDefs
{
    int count, i;
    struct ticmark *cur;
    id tic;

    switch (s_ticTypeCoord[X_TAG] = xticdef.type) {
    case TIC_SERIES:
	s_ticSeriesCoord[X_TAG].start = xticdef.def.series.start;
	s_ticSeriesCoord[X_TAG].incr = xticdef.def.series.incr;
	s_ticSeriesCoord[X_TAG].end = xticdef.def.series.end;
	break;
    case TIC_USER:
	count = [ticDefs[X_TAG] count] - 1;
	i = 0;
	for (cur = xticdef.def.user ; cur != NULL ; cur = cur->next) {
	    if (i <= count) {
		[tic = [ticDefs[X_TAG] objectAt:i]
	       	       setDoubleValue:cur->position];
		[tic setStringValue:cur->label];
	    } else {
		[ticDefs[X_TAG] addObject:[[TicObject allocFromZone:zone]
						initFromString:cur->label
						   doubleValue:cur->position]];
	    }
	    i++;
	}
	break;
    }

    switch (s_ticTypeCoord[Y_TAG] = yticdef.type) {
    case TIC_SERIES:
	s_ticSeriesCoord[Y_TAG].start = yticdef.def.series.start;
	s_ticSeriesCoord[Y_TAG].incr = yticdef.def.series.incr;
	s_ticSeriesCoord[Y_TAG].end = yticdef.def.series.end;
	break;
    case TIC_USER:
	count = [ticDefs[Y_TAG] count] - 1;
	i = 0;
	for (cur = yticdef.def.user ; cur != NULL ; cur = cur->next) {
	    if (i <= count) {
		[tic = [ticDefs[Y_TAG] objectAt:i]
		       setDoubleValue:cur->position];
		[tic setStringValue:cur->label];
	    } else {
		[ticDefs[Y_TAG] addObject:[[TicObject allocFromZone:zone]
						initFromString:cur->label
						   doubleValue:cur->position]];
	    }
	    i++;
	}
	break;
    }

    switch (s_ticTypeCoord[Z_TAG] = zticdef.type) {
    case TIC_SERIES:
	s_ticSeriesCoord[Z_TAG].start = zticdef.def.series.start;
	s_ticSeriesCoord[Z_TAG].incr = zticdef.def.series.incr;
	s_ticSeriesCoord[Z_TAG].end = zticdef.def.series.end;
	break;
    case TIC_USER:
	count = [ticDefs[Z_TAG] count] - 1;
	i = 0;
	for (cur = zticdef.def.user ; cur != NULL ; cur = cur->next) {
	    if (i <= count) {
		[tic = [ticDefs[Z_TAG] objectAt:i]
		     setDoubleValue:cur->position];
		[tic setStringValue:cur->label];
	    } else {
		[ticDefs[Z_TAG] addObject:[[TicObject allocFromZone:zone]
			       initFromString:cur->label 
				  doubleValue:cur->position]];
	    }
	    i++;
	}
	break;
    }



    return self;
}


/*  
 *  We use a list of FunctionObjects to store each function (a 
 *  "function" is a mathematical expression that gnuplot will evaluate 
 *  at certain points or a data file containing pairs/triples of 
 *  numbers on each line).  We create these objects by parsing the 
 *  plot arguments.
 */
- grabFunctionsFrom: (char *) aString
{
    int count;
    FunctionObject *aFunction;
    char *src, *end;
    BOOL done;

    if ([functions count])
	[functions freeObjects];

    if (!aString)
	return nil;

    /* Skip any range specifications */
    if (src = rindex (aString, ']'))
	aString = src + 1;

    /* Count the functions */
    count = 1;
    src = aString;
    while (src = index(src, ','))  {
	count ++;
	src++;
    }

    /* Copy them to the strings list */

    src = aString;
    done = NO;
    while (!done)  {
	if (end = index (src, ','))
	    *end = '\0';
	else
	    done = YES;

	aFunction = [[FunctionObject allocFromZone:zone]
		 initFromString: _removeLeadingBlanks (src)];
	[functions addObject:aFunction];

	if (!done) {
	    *end = ',';
	    src = ++end;
	}
    }

    return self;
}




- setThreeD:(BOOL) aCondition
{

    if (isThreeD != aCondition)  {

	isThreeD = aCondition;

	if (s_parametric) {
	    if (isThreeD) {
		[self _copyString:"u" to:&(s_dummy_var[X_TAG])];
		[self _copyString:"v" to:&(s_dummy_var[Y_TAG])];
	    } else
		[self _copyString:"t" to:&(s_dummy_var[X_TAG])];
	}

	[self reportSettingsChange:self];
    }
    return self;
}


- (BOOL) isThreeD
{
    return isThreeD;
}



- setParametric:(BOOL) cond
{
    if (s_parametric != cond)  {

	s_parametric = cond;

	if (cond) {
	    if (isThreeD) {
		[self _copyString:"u" to:&(s_dummy_var[X_TAG])];
		[self _copyString:"v" to:&(s_dummy_var[Y_TAG])];
	    } else
		[self _copyString:"t" to:&(s_dummy_var[X_TAG])];
	} else {
	    [self _copyString:"x" to:&(s_dummy_var[X_TAG])];
	    [self _copyString:"y" to:&(s_dummy_var[Y_TAG])];
	}

	[self reportSettingsChange:self];

    }
    return self;
}


- (BOOL) parametric
{
    return s_parametric;
}



- setPolar:(BOOL) cond
{
    if (s_polar != cond) {
	s_polar = cond;

	if (cond) {
	    s_minCoord[X_TAG] = 0;
	    s_maxCoord[X_TAG] = degrees? 360 :6.283186;
	    s_isLogCoord[X_TAG] = 0;
	} else {
	    s_minCoord[X_TAG] = -10.0;
	    s_maxCoord[X_TAG] = 10.0;
	}
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) isPolar
{
    return s_polar;
}



- setBorder:(BOOL) cond
{
    if (s_border != cond) {
	s_border = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) border
{
    return s_border;
}



- setSurface:(BOOL) cond
{
    if (s_draw_surface != cond) {
	s_draw_surface = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) surface
{
    return s_draw_surface;
}



- setHiddenThreeD:(BOOL) cond
{
    if (s_hidden3d != cond) {
	s_hidden3d = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) hiddenThreeD
{
    return s_hidden3d;
}



- setGrid:(BOOL) cond
{
    if (s_grid != cond) {
	s_grid = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) grid
{
    return s_grid;
}



- setAxisX:(int) anInt
{
    if (s_xzeroaxis != anInt) {
	s_xzeroaxis = anInt;
	[self reportSettingsChange:self];
    }

    return self;
}


- (int) axisX
{
    return s_xzeroaxis;
}



- setAxisY:(int) anInt
{
    if (s_yzeroaxis != anInt) {
	s_yzeroaxis = anInt;
	[self reportSettingsChange:self];
    }

    return self;
}


- (int) axisY
{
    return s_yzeroaxis;
}



- setIsLogCoord:(int)coord isOn:(BOOL)isOn
{
    if (s_isLogCoord[coord] != isOn) {
	s_isLogCoord[coord] = isOn;
	[self reportSettingsChange:self];
    }
    return self;
}



- (BOOL) isLogCoord:(int)coord
{
    return s_isLogCoord[coord];
}



- setTitle:(const char *) aString
{
    [self _copyString:aString to:&s_title];
    [self reportSettingsChange:self];

    return self;
}


- (const char *) title
{
    return s_title;
}


- setLabelCoord:(int)coord to:(const char *) aString
{
    [self _copyString:aString to:&(s_labelCoord[coord])];
    [self reportSettingsChange:self];

    return self;
}



- (const char *) labelCoord:(int) coord
{
    return s_labelCoord[coord];
}


- setDummyVar:(int)coord to:(const char *)aString
{
    [self _copyString:aString to:&(s_dummy_var[coord])];
    [self reportSettingsChange:self];
    return self;
}


- (const char *)dummyVar:(int)coord
{
    if (!s_dummy_var[coord])
	switch (coord) {
	case X_TAG:
	    return "x";
	    break;
	case Y_TAG:
	    return "y";
	    break;
	}

    return s_dummy_var[coord];
}


- setAppendix: (const char *) aString
{
    NXZoneFree (zone, appendix);

    if (aString)
	appendix = NXCopyStringBufferFromZone (aString, zone);
    else
	appendix = NULL;

    return self;
}


- setFontsize: (int) anInt;
{
    if ((s_fontsize != anInt) && (anInt > 0)) {
	s_fontsize = anInt;
	[self reportSettingsChange:self];
	return self;
    } else
	return nil;
}


- (int) fontsize
{
    return s_fontsize;
}



- setFontname: (const char *) aString
{
    [self _copyString:aString to:&s_font];
    [self reportSettingsChange:self];

    return self;
}


- (const char *) fontname
{
    return s_font;
}



- setFont:aFont
{
    if (aFont)  {
	[self _copyString:[aFont name] to:&s_font];
	s_fontsize = [aFont pointSize];
	[self reportSettingsChange:self];
    }

    return self;
}



- font
{
    if (!s_font)
	return [Font newFont:DEFAULT_FONT size:s_fontsize];
    return [Font newFont:s_font size:s_fontsize];
}



- setKey:(BOOL) cond
{
    if (s_key != cond) {
	s_key = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) key
{
    return s_key;
}


- setKeyDefault:(BOOL) cond
{
    if (s_key_default != cond) {
	s_key_default = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) keyDefault
{
    return s_key_default;
}


- setKeyCoord:(int)coord to:(double)aDouble
{
    if (s_keyCoord[coord] != aDouble) {
	s_keyCoord[coord] = aDouble;
	[self reportSettingsChange:self];
    }
    return self;
}


- (double) keyCoord:(int)coord
{
    return s_keyCoord[coord];
}



- setTime:(BOOL) cond
{
    if (s_time != cond) {
	s_time = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) time
{
    return s_time;
}


- setTimeDefault:(BOOL) cond
{
    if (s_time_default != cond) {
	s_time_default = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) timeDefault
{
    return s_time_default;
}


- setTimeCoord:(int)coord to:(int)aDouble
{
    if (s_timeCoord[coord] != aDouble) {
	s_timeCoord[coord] = aDouble;
	[self reportSettingsChange:self];
    }
    return self;
}


- (int) timeCoord:(int)coord
{
    return s_timeCoord[coord];
}



- setSizeProp:(BOOL) cond
{
    sizeProp = cond;		/* Don't do anything, just record status */
    return self;
}


- (BOOL)sizeProp
{
    return sizeProp;
}


/*
 *  Size of zero means don't change... (e.g. if x=2 and y=0, we change 
 *  x but not y).
 */
- setSizeX:(float) xFloat Y:(float) yFloat
{
    if ((xFloat != s_xsize) || (yFloat != s_ysize)) {

	if (xFloat)
	    s_xsize = xFloat;

	if (yFloat)
	    s_ysize = yFloat;

	[self reportSettingsChange:self];
    }

    return self;
}


- (float) sizeX
{
    return s_xsize;
}


- (float) sizeY
{
    return s_ysize;
}



- setRotCoord:(int)coord to:(float)aFloat
{
    if (s_rotCoord[coord] != aFloat) {
	s_rotCoord[coord] = aFloat;
	[self reportSettingsChange:self];
    }
    return self;
}



- resetRotation
{
    s_rotCoord[X_TAG] = 60.0;
    s_rotCoord[Z_TAG] = 30.0;
    [self reportSettingsChange:self];
    return self;
}



- (float)rotCoord:(int)coord
{
    return s_rotCoord[coord];
}



- setSamplesCoord:(int)coord to:(int)anInt
{
    if ((anInt > 0) && (anInt != s_samplesCoord[coord])) {
	s_samplesCoord[coord] = anInt;
	if (coord == X_TAG)
	    s_samples = anInt;
    } else
	return nil;

    [self reportSettingsChange:self];
    return self;
}


- (int) samples:(int)coord
{
    return s_samplesCoord[coord];
}



- setIsoSamplesCoord:(int)coord to:(int)anInt
{
    if ((anInt > 0) && (anInt != s_iso_samplesCoord[coord]))
	s_iso_samplesCoord[coord] = anInt;
    else
	return nil;

    [self reportSettingsChange:self];
    return self;
}


- (int) isoSamples:(int)coord
{
    return s_iso_samplesCoord[coord];
}



- setAutoscaleCoord:(int)coord isOn:(BOOL)cond
{
    if (s_autoscaleCoord[coord] != cond) {
	s_autoscaleCoord[coord] = cond;
	[self reportSettingsChange:self];
    }
    return self;
}


- (BOOL)autoscaleCoord:(int)coord
{
    return s_autoscaleCoord[coord];
}


- setRangeCoord:(int)coord min:(double)min max:(double)max
{
    if ((s_maxCoord[coord] != max) || (s_minCoord[coord] != min)) {

	s_maxCoord[coord] = max;
	s_minCoord[coord] = min;

	[self reportSettingsChange:self];

    }
    return self;
}

- (double)minCoord:(int)coord
{
    return s_minCoord[coord];
}

- (double)maxCoord:(int)coord
{
    return s_maxCoord[coord];
}



- setDegrees:(BOOL)cond;
{
    if (degrees != cond) {
	degrees = cond;
	if (s_polar)
	    if (degrees)  {		/* Convert radians to degrees	*/
		s_maxCoord[X_TAG] *= 57.2957795131;
		s_minCoord[X_TAG] *= 57.2957795131;
	    } else {			/* Convert degrees to radians	*/
		s_maxCoord[X_TAG] /= 57.2957795131;
		s_minCoord[X_TAG] /= 57.2957795131;
	    }	

	[self reportSettingsChange:self];
    }
    return self;
}


- (BOOL)degrees
{
    return degrees;
}



/*
 *  We plot by assembling a Gnuplot "plot" command from the list of
 *  FunctionObjects.
 */

- buildPlotargs
{
    int index = [functions count];
    const char *aString;
    FunctionObject *function;
    int size = 0, counter, anInt, anInt2;

    /*  
     *  First, we calculate the total size that will be required for 
     *  the entire plot command, including commas, etc.
     */
    if (counter = index)  {

	size = isThreeD? 6 : 5;				/* "splot" or "plot" */

	while (counter--) {
	    function = [functions objectAt:counter];

	    if (aString = [function stringValue])
		size += strlen (aString);

	    if ([function isDataFile])
		size += 2;				/* 2 quotation marks */

	    if (aString = [function title])
		size += (strlen (aString) + 9);		/* " title 'xxx'"    */

	    if (aString = [function styleString])
		size += (strlen (aString) + 12);	/* " with ??? xx xx" */

	    size += 2;					/* comma and space   */
	}

	/* 
	 *  No comma or space after the last functions	= -2
	 *  Terminating ASCII NULL			= +1
	 * 						----
	 * 				Total		= -1
	 */
	
	size--;
    }

    /*  Then we malloc enough space.  */

    NXZoneFree (zone, s_plotargs);
    s_plotargs = NXZoneMalloc (zone, size);


    /*  And finally, assemble the pieces.  */
    if (index) {
	if (isThreeD)
	    strcpy (s_plotargs, "splot ");
	else
	    strcpy (s_plotargs, "plot ");

	counter = 0;
	while (counter != index)  {
	    function = [functions objectAt:counter++];

	    if ([function isDataFile])
		sprintf (index (s_plotargs, '\0'), "'%s'",
			 [function stringValue]);
	    else
		s_plotargs = strcat (s_plotargs, [function stringValue]);

	    /* 
	     *  We determine which kind of quotation marks to use for 
	     *  the title by checking to see if the title itself 
	     *  contains any quotation marks and using the opposite 
	     *  kind.  Assumption:  that the title does not use both 
	     *  kinds of quotation marks, single and double.
	     */
	    if (aString = [function title]) {
		if (index (aString, '"'))
		    sprintf (index (s_plotargs, '\0'), " title '%s'", aString);
		else
		    sprintf(index(s_plotargs, '\0'), " title \"%s\"", aString);
	    }

	    if (aString = [function styleString])  {

		sprintf (index (s_plotargs, '\0'), " with %s", aString);

		/* 
		 *  The following logical gymnastics are required 
		 *  because the specific line and point styles may or 
		 *  may not have been specified by the user and may or 
		 *  may not be relevant.  Plus, Gnuplot requires that 
		 *  the line style be specified whenever the point 
		 *  style is specified, even if the function has no 
		 *  lines.  Basically, we specify what we must (based 
		 *  on the user's choices) and let the rest go to the 
		 *  default.
		 */
		
		switch ([function style]) {
		case FUNCTION_LINES:
		    if ((anInt = [function lineStyle]) != LINE_NOSTYLE)
			sprintf (index (s_plotargs, '\0'), " %d", anInt);
		    break;
		case FUNCTION_POINTS:
		    if ((anInt = [function pointsStyle]) != POINTS_NOSTYLE)
			sprintf (index (s_plotargs, '\0'), " 1 %d", anInt);
		    break;
		case FUNCTION_LINESPOINTS:
		    anInt2 = [function pointsStyle];
		    anInt = [function lineStyle];
		    if ((anInt != LINE_NOSTYLE) || 
			(anInt2 != POINTS_NOSTYLE)) {
			sprintf (index (s_plotargs, '\0'), " %d", anInt);
			if (anInt2 != POINTS_NOSTYLE)
			    sprintf (index (s_plotargs, '\0'), " %d", anInt2);
		    }
		    break;
		}

	    }
		
	    /* If there are more functions, we need a comma */
	    if (counter != index)
	        s_plotargs = strcat (s_plotargs, ", ");
	}
    }

    return self;
}


- (const char *) plotargs
{
    return s_plotargs;
}



- (List *)functions
{
    return functions;
}


// Shuts up the compiler about unused RCSId
- (const char *) rcsid
{
    return RCSId;
}


@end


@implementation Status (Private)


- (const char *) _copyString:(const char *)source to:(char **)dest
{
    if (dest)  {
	NXZoneFree (zone, *dest);

	if (source)
	    *dest = NXCopyStringBufferFromZone (source, zone);
	else
	    *dest = NULL;
    }

    return *dest;
}


/*
 *  This method is nearly identical to load_file in misc.c, except 
 *  that instead of reading commands from a file, it reads from a 
 *  string, passing one line at a time to gnuplot.
 */
- _doText:(const char *)theText
{
    extern char input_line[];		/* Line to be read by do_line()	*/
    extern jmp_buf env;
    extern int do_line();
    extern int interactive;

    int len;				/* The length of a command	*/
    int start, max;			/* Indexes in input_line string	*/
    BOOL more;				/* Is there more of this line?	*/
    BOOL stop;				/* Are we done reading text?	*/
    int inline_num;			/* Text line number...		*/
    int inline_index;			/* ... which begins here	*/


    if (setjmp(env))  {			/*  Gnuplot bails out to here	*/
					/*  in case of parse error, etc.*/
	if (s_epsStream) {

	    NXCloseMemory (s_epsStream, NX_FREEBUFFER);
	    s_epsStream = NULL;

	}

	return nil;

    } else {

	interactive = 0;
	max = MAX_LINE_LEN;
	stop = NO;
	inline_num = inline_index = start = 0;

	if (s_epsStream)
	    NXCloseMemory (s_epsStream, NX_FREEBUFFER);
	s_epsStream = NXOpenMemory(NULL, 0, NX_READWRITE);

	EPSStream = s_epsStream;

	while (!stop) {              	/* Read every line in theText	*/

	    more = YES;			/* Read one command		*/
	    while (more) {
		if (!_get_line (&(input_line[start]), max,
				&theText[inline_index])) {
		    stop = YES;		/* End of string		*/
		    more = NO;
		}

		inline_num++;
		len = strlen (input_line);
		inline_index += (len + 1);
		if (len+1 >= max) {
		    fprintf (stderr, "Input line too long\n");
		    /* todo Bring up a modal panel here or something */
		}

		                        /* Continuation line		*/
		if (input_line[len - 1] == '\\') {
		    start = len - 1;
		    max -= start;
		} else
		    more = NO;
	    }

	    if (strlen (input_line) > 0) {
		do_line();			/* Parse input_line	*/
	    }

	}

	NEXTFE_reset();

	return self;
    }
}



- _setText: (const char *) aString
{
    NXZoneFree (zone, text);

    /* Magic number 100 -- todo be more accurate */
    text = NXZoneMalloc (zone, strlen (aString) + 130);
    sprintf (text, "set term nextfe '%s' %d\nset output '/dev/null'\npi = 3.14159265359\n%s",
	     s_font ? s_font : DEFAULT_FONT, s_fontsize, aString);

    return self;
}




@end
